package calendar;

import java.awt.Dimension;
import java.io.IOException;
import java.util.Date;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import javax.swing.JFrame;
import javax.swing.UIManager;
import javax.swing.plaf.metal.DefaultMetalTheme;
import javax.swing.plaf.metal.MetalLookAndFeel;

/* main calendar client class */
public class CalendarClient implements CalEventListener
{
  ClientPanel clientPanel;
//  Registry registry  ;
  CalendarObjInt RemCalendarObj = null;
  CalendarMgrInt RemCalendarMgr = null;
  Callback callback = null;
  String strName;
  User user;
  boolean connected = false;
  String host;
  int port;
  
  public CalendarClient( String host, String port ) throws IOException
  {
    super();
    this.host = host;
    this.port = (int)Integer.parseInt(port.trim());

    /* setup the security manager and obtain the manager object from registry */
    strName = "CalendarService2011";
    System.out.println("Client: Looking up " + strName + "...");    
    try
    {
      RemCalendarMgr = new CalendarMgrStub( host, this.port, this.port, strName );
    } 
    catch (Exception e)
    {
      System.out.println("CalendarClient: Constructor: " + strName + "-> " + e);
      System.exit(1);
    }
    callback = new Callback();
  }

  /* set the client panel object for the client object */
  public void setClientPanel( ClientPanel clientPanel )
  {
    this.clientPanel = clientPanel;
    this.clientPanel.setUsers( this.list() );
    callback.setClientPanel( this.clientPanel );
  }

  /* connect to a calendar via the remote calendar object interface */
  public CalendarObjInt connectCalendar( User user )
  {
    boolean result = false;
    strName = "Calendar" + user.getId();

    try
    {
      result = RemCalendarMgr.connectCalendar( user );
    }
    catch (Exception ex)
    {
      System.out.println( "(ERROR) Invalid calendar id specified." );
    }
    
    User aUser = this.findUser( user );
    
    try
    {
      RemCalendarObj = new CalendarStub( host, aUser.getPort(), aUser.getPort(), strName );
    }
    catch (Exception e)
    {
      System.out.println( "(ERROR) Invalid calendar id specified." );
    }

    /* keep track of connection status */
    if ( result ) this.connected = true;

    if ( result ) return RemCalendarObj;
    else return null;
  }

  /* disconnect from a remote calendar object */
  public boolean disconnectCalendar( User user )
  {
    boolean result = false;
    try
    {
      RemCalendarObj.unregisterCallback();
      result = RemCalendarMgr.disconnectCalendar( user );
    }
    catch (Exception e)
    {
      System.out.println("CalendarClient: disconnectCalendar -> " + e);
      System.exit(1);
    }

    if ( result ) this.connected = false;

    return result;
  }

  /* create a new remote calendar object via the remote calendar manager */
  public boolean createCalendar( User user )
  {
    boolean result = false;
    try
    {
       result = RemCalendarMgr.createCalendar( user );
    }
    catch (Exception e)
    {
      System.out.println("CalendarClient: createCalendar -> " + e);
      System.exit(1);
    }
    return result;
  }

  /* schedule an event in the calendar */
  public ArrayList scheduleEvent( int type, String desc, Date start, Date end,
                                  ArrayList users, User owner, Event oldEvent )
  {
    boolean addToThis = true;

    Event event = new Event( type, desc, start, end, owner );

    if ( type == 3 ) addToThis = false;
    
    ArrayList result = new ArrayList();
    try
    {
      if( RemCalendarObj != null && RemCalendarObj.isConnected() )
      {
        result = RemCalendarObj.scheduleEvent( users, event, oldEvent,
          addToThis );
      }
    }
    catch(IOException ex)
    {
      System.out.println("CalendarClient: scheduleEvent -> " + ex);
    }
   
    return result;
  }

  /* retreive a list of events based on user and start/end dates */
  public ArrayList retrieveEvents( User user, Date start, Date end )
  {
    ArrayList result = new ArrayList();
    try
    {
      if( RemCalendarObj != null && RemCalendarObj.isConnected() )
      {
        result = RemCalendarObj.retrieveEvents( user, start, end );
      }
    }
    catch(IOException ex)
    {
      System.out.println("CalendarClient: retrieveEvents -> " + ex);
    }

    return result;
  }

  /* returns the current list of calendars (users) */
  public ArrayList list()
  {
    ArrayList users = new ArrayList();
    try
    {
        users = RemCalendarMgr.list();
    }
    catch (Exception e)
    {
      System.out.println( "Client: Unable to connect to Calendar Server." );
      e.printStackTrace();
      //System.exit(1);
    }

    return users;
  }

  /* returns status of this client's connection */
  public boolean isConnected()
  {
    return connected;
  }
  
  public User findUser( User user )
  {
    ArrayList users = this.list();
    User theUser = null;
    for (int i = 0; i < users.size(); i++)
    {
        User aUser = (User)users.get(i);
        if ( aUser.getId() == user.getId() )
        {
          theUser = aUser;
          break;
        }
    }  
    return theUser;
  }

  public void calEventOccurred( CalEvent calEvent ) {

    /* all events end up here, forwarded to appropriate remote object */
    if ( calEvent.getEventId() == CalEvent.CLOSE )
    {
      User aUser = null;
      try
      {
        if ( this.isConnected() )
          aUser = clientPanel.getCalendar().getUser();
      } 
      catch (IOException ex)
      {
        System.out.println("CalendarClient: calEvent(CLOSE) -> " + ex);
      }
      if ( this.isConnected() ) disconnectCalendar( aUser );
      System.exit( 0 );
    }

    /* perform necessary actions to create a new calendar */
    else if ( calEvent.getEventId() == CalEvent.CREATE )
    {
      String alert = "";
      String userName = clientPanel.getUserName();
      user = User.makeUser( userName, clientPanel.getUsers() );
      boolean result = createCalendar( user );
      if ( result )
      {
        alert = "Created new calendar for '" + user.getName() +
                     "'.  Your user id is:  " + user.getId();
        clientPanel.setUsers( list() );
        clientPanel.clearCreateText();
      }
      else
      {
        alert = "Error:  Could not create calendar for '" + user.getName() +
                     "'. This name is already in use (or you entered nothing)." +
                     " Please try again.";
      }
      clientPanel.addAlert( alert );
    }

    /* perform necessary actions to connect to a calendar */
    else if ( calEvent.getEventId() == CalEvent.CONNECT )
    {
        
        int panelUserId = clientPanel.getUserId();
        String panelUserName = ""; 
        if ( panelUserId <= 0 )
        {
          String message = "Error:  You must enter a positive integer for " +
            "the User ID.";
          clientPanel.addAlert( message );
          return;
        }
        
        User panelUser = new User( panelUserId, panelUserName );

        /* check to see if already connected */
        if ( this.isConnected() )
        {
          User user2 = null;
          try
          {
            user2 = clientPanel.getCalendar().getUser();
          }
          catch ( IOException ex )
          {
            this.connected = false;
            System.out.println( "(ERROR) Could not connect to calendar for user requested." );
          }
          /* disconnected the current calendar if one is connected */
          boolean result = disconnectCalendar( user2 );
          if ( result )
          {
            clientPanel.setDisconnected();
            clientPanel.resetEventList();
            clientPanel.clearEventPanel();
            try
            {
              clientPanel.addAlert("Disconnected from "
                + clientPanel.getCalendar().getUser().getName()
                + "'s calendar.");
            }
            catch ( IOException ex )
            {
              System.out.println( "CalendarClient: calEvent(CONNECT) -> "
                + ex );
            }
          }
        }

        CalendarObjInt calendar = null;
        /* connect to the specified calendar and register for callbacks */
        try
        {
          calendar = connectCalendar(panelUser);
        }
        catch (Exception ex) {
         System.out.println( "CalendarClient: calEvent(CONNECT) -> " + ex );
        }
        if (calendar != null ) 
        {
          clientPanel.setCalendar(calendar);
          clientPanel.resetEventList();
          clientPanel.clearEventPanel();
          try 
          {
            calendar.registerCallback( callback );
            clientPanel.setConnectName( calendar.getUser().getName() );
            /*clientPanel.setUserChecked( calendar.getUser() );*/
            clientPanel.addAlert("Connected to "
              + calendar.getUser().getName() + "'s calendar.  " +
              "Check the boxes next to user names to see their event lists." );
          }
          catch (Exception e)
          {
            System.out.println("CalendarClient: calEvent(CONNECT) -> " + e);
          }
        }
        else
        {
          clientPanel.addAlert("Error:  Cannot connect due to invalid user" +
            " id or someone already connected to this calendar");
        }

        /* clear out the users selected and the status text at the top */
        clientPanel.clearSelectedUsers();
        clientPanel.clearConnectText();
    }

    /* perform necessary actions to disconnect from a calendar */
    else if ( calEvent.getEventId() == CalEvent.DISCONNECT )
    {
      if ( !this.isConnected() )
      {
        clientPanel.addAlert("You are not currently connected to a calendar.");
        return;
      }
       
      User dUser = null;
      try
      {
        dUser = clientPanel.getCalendar().getUser();
      } 
      catch ( IOException ex )
      {
        System.out.println("CalendarClient: calEvent(DISCONNECT) -> " + ex);
      }
      boolean result = disconnectCalendar( dUser );
      if ( result )
      {
        clientPanel.setDisconnected();
        clientPanel.resetEventList();
        clientPanel.clearEventPanel();
        try 
        {
          clientPanel.addAlert("Disconnected from "
            + clientPanel.getCalendar().getUser().getName() + "'s calendar.");
        }
        catch ( IOException ex )
        {
          System.out.println("CalendarClient: calEvent(DISCONNECT) -> " + ex);
        }
      }
      else
      {
        clientPanel.addAlert( "Error:  You entered an invalid user id " +
                              "or you are not connected to this calendar." );
      }

      /* clear out necessary windows from client panel */
      clientPanel.clearSelectedUsers();
      clientPanel.clearEventPanel();
    }

    /* perform necessary actions to list the user calendars */
    else if ( calEvent.getEventId() == CalEvent.LIST_USERS )
    {
      clientPanel.setUsers( list() );
    }

    /* perform necessary actions to list the events of the selected users */
    else if ( calEvent.getEventId() == CalEvent.LIST_EVENTS )
    {
      if ( this.isConnected() )
      {
        clientPanel.resetEventList();
        ArrayList selectedUsers = clientPanel.getSelectedUsers();
        clientPanel.listUserEvents( selectedUsers );
      }
    }

    /* perform necessary actions to show a selected event */
    else if ( calEvent.getEventId() == CalEvent.SHOW_EVENT )
    {
      if ( this.isConnected() )
      {
        try
        {
          clientPanel.showEvent(clientPanel.getSelectedEvent(),
          clientPanel.getCalendar().getUser().getId() );
        }
        catch(IOException ex)
        {
          System.out.println("CalendarClient: calEvent(SHOW_EVENT) -> " + ex);
        }
      }
    }

    /* perform the necessary actions to create or edit an event */
    else if ( calEvent.getEventId() == CalEvent.CREATE_EVENT ||
              calEvent.getEventId() == CalEvent.EDIT_EVENT )
    {
      String message = new String();

      /* check to see if currently connected to a calendar */
      if ( !this.isConnected() )
      {
        message = "Error:  You must connect to a calendar before you can " +
          "add any events.";
        clientPanel.addAlert( message );
        return;
      }
      
      ArrayList usersBooked = new ArrayList();
      ArrayList selectedUsers = new ArrayList();
      User currentUser = null;
      try
      {
        currentUser = clientPanel.getCalendar().getUser();
      }
      catch (IOException ex)
      {
          System.out.println("CalendarClient: calEvent(CREATE/EDIT) -> " + ex);
      }

      Event oldEvent = null;
      /* check for permission to edit an event */
      if ( calEvent.getEventId() == CalEvent.EDIT_EVENT )
      {
        boolean permission = false;
        oldEvent = clientPanel.getSelectedEvent();
        if ( oldEvent.getOwnerId() == currentUser.getId() ||
             (oldEvent.getType() == 3 &&
             oldEvent.isParticipant( currentUser.getId() ) ) )
          permission = true;

        if ( !permission )
        {
          message = "Error:  You do not have permission to edit this event.";
          clientPanel.addAlert( message );
          return;
        }
      }

      /* check for CREATE_EVENT event and buttons are disabled */
      if ( calEvent.getEventId() == CalEvent.CREATE_EVENT &&
           clientPanel.areCreateEventPanelButtonsDisabled() )
      {
        message = "Error:  The event type buttons are disabled.  They have " +
          "been re-enabled.  Please try again.";
        clientPanel.addAlert( message );
        clientPanel.setCreateEventPanelButtonsEnabled();
        clientPanel.resetEventList();
        return;
      }

      /* check for unselected type */
      if ( clientPanel.getNewEventType() == 0 )
      {
        message = "Error:  You did not select an event type. Please try again.";
        clientPanel.addAlert( message );
        return;
      }

      /* check for a start date that has already passed */
      Calendar cal = Calendar.getInstance();
      Date now = cal.getTime();
      if ( clientPanel.getNewEventStartDate().compareTo( now ) <= 0 )
      {
        message = "Error:  The start date cannot be in the past. " +
          "Please try again.";
        clientPanel.addAlert( message );
        return;
      }

      /* check for end date before start date */
      Date temp = new Date();
      temp = clientPanel.getNewEventStartDate();
      if ( clientPanel.getNewEventEndDate().compareTo( temp ) <= 0 )
      {
        message = "Error:  The event end date must be after the event start " +
          "date. Please try again.";
        clientPanel.addAlert( message );
        return;
      }

      /* check for users selected if group event selected */
      if ( clientPanel.getNewEventType() == 3 )
      {
        boolean selfSelected = false;
        Iterator itr = clientPanel.getSelectedUsers().iterator();
        while ( itr.hasNext() )
        {
          User u = (User)itr.next();
          if ( u.getId() == currentUser.getId() ) selfSelected = true;
        }
        if ( selfSelected && clientPanel.getSelectedUsers().size() == 1 )
        {
          message = "Error:  You selected a group event, but did not select " +
            "any participants besides yourself.";
          clientPanel.addAlert( message );          
          return;
        }
        else if ( clientPanel.getSelectedUsers().isEmpty() )
        {
          message = "Error:  You selected a group event, but did not select " +
            "any participants.";
          clientPanel.addAlert( message );
          return;
        }
      }

      /* get the users selected to be in this event if it's a group event */
      if ( clientPanel.getNewEventType() == 3 )
      {
        selectedUsers = clientPanel.getSelectedUsers();
        Iterator itrUsers = selectedUsers.iterator();
        boolean selfSelected = false;
        while ( itrUsers.hasNext() )
        {
          User u = (User)itrUsers.next();
          if ( u.getId() == currentUser.getId() ) selfSelected = true;
        }
        if ( !selfSelected )
        {
          message = "Error: You must select yourself for group events.";
          clientPanel.addAlert( message );
          return;
        }
      }

      usersBooked = scheduleEvent( clientPanel.getNewEventType(),
                                   clientPanel.getNewEventDesc(),
                                   clientPanel.getNewEventStartDate(),
                                   clientPanel.getNewEventEndDate(),
                                   selectedUsers, currentUser,
                                   oldEvent );

      /* determine which users were added into this event */
      if ( clientPanel.getNewEventType() == 3 )
      {
        String booked = "";
        String notBooked = "";
        Iterator itr = selectedUsers.iterator();
        while ( itr.hasNext() )
        {
          User u = (User)itr.next();
          boolean isBooked = false;
          Iterator itrBooked = usersBooked.iterator();
          while ( itrBooked.hasNext() )
          {
            User uBooked = (User)itrBooked.next();
            if ( u.getId() == uBooked.getId() )
            {
              isBooked = true;
              if ( booked.compareTo( "" ) == 0 ) 
                 booked = booked + u.getName();
              else booked = booked + ", " + u.getName();
            }
          }
          if ( !isBooked )
          {
            if ( notBooked.compareTo( "" ) == 0 )
              notBooked = notBooked + u.getName();
            else notBooked = notBooked + ", " + u.getName();
          }
        }

        if ( usersBooked.size() > 0 )
        {
          if ( calEvent.getEventId() == CalEvent.EDIT_EVENT )
            message = "The event was edited for these calendar(s): ";
          else
            message = "The event was successfully added to the calendar(s):  ";
          message = message + booked + ".";
          clientPanel.addAlert( message );

          if ( notBooked.compareTo( "" ) != 0 )
          {
            message = "The following user(s) were not available for " +
              "the entire event:  " + notBooked + ".";
            clientPanel.addAlert( message );
          }
          /* this will refresh the current event list */
          clientPanel.setCalendar( clientPanel.getCalendar() );
        }
        else 
        {
          if ( calEvent.getEventId() == CalEvent.EDIT_EVENT )
            message = "The event was deleted.  None of the users "
              + "were available for the new event duration.";
          else
            message="The event was not created.  None of the users "
              + "were available for the entire event.";
          clientPanel.addAlert( message );
        }
      }
      else
      {
        if ( usersBooked.size() == 1 )
        {
          if ( calEvent.getEventId() == CalEvent.EDIT_EVENT )
            message = "The event was sucessfully edited for your calendar.";
          else
            message = "The event was successfully added to your calendar";
          /* this will refresh the current event list */
          clientPanel.setCalendar( clientPanel.getCalendar() );
        }
        else
        {
          if ( calEvent.getEventId() == CalEvent.EDIT_EVENT )
            message = "The event could not be edited due to a conflict.";
          else
            message = "The event could not be added to your calendar due to "
              + "a conflict.";
        }
        clientPanel.addAlert( message );
      }
      try
      {
        clientPanel.getCalendar().clearMembers();
      }
      catch(IOException ex)
      {
        System.out.println("CalendarClient: calEvent(CREATE/EDIT) -> " + ex);
      }
      clientPanel.resetEventList();
      clientPanel.clearEventPanel();
      selectedUsers = clientPanel.getSelectedUsers();
      clientPanel.listUserEvents( selectedUsers );
      clientPanel.setCreateEventPanelButtonsEnabled();
    }

    /* perform necessary actions to delete an event */
    else if ( calEvent.getEventId() == CalEvent.DELETE_EVENT )
    {
      boolean addToThis = true;
      ArrayList users = new ArrayList();
      users = clientPanel.getUsers();
      if ( clientPanel.getSelectedEvent().getType() == 3 ) addToThis = false;

      try
      {
        if( RemCalendarObj != null && RemCalendarObj.isConnected() )
        {
          RemCalendarObj.scheduleEvent( users, null,
            clientPanel.getSelectedEvent(), addToThis );
        }
      }
      catch(IOException ex)
      {
        System.out.println("CalendarClient: calEvent(DELETE_EVENT) -> " + ex);
      }
      clientPanel.addAlert( "The event has been removed from your calendar." );
      clientPanel.clearEventPanel();
      clientPanel.resetEventList();
      clientPanel.clearSelectedUsers();
    }

    /* focus on the connect button so the enter key can be used */
    else if ( calEvent.getEventId() == CalEvent.FOCUS_CONNECT )
    {
      clientPanel.setFocusConnect();
    }

    /* focus on the create button so the enter key can be used */
    else if ( calEvent.getEventId() == CalEvent.FOCUS_CREATE )
    {
      clientPanel.setFocusCreate();
    }

    /* reset the create event panel for anything besides show or list events */
    if ( calEvent.getEventId() != CalEvent.SHOW_EVENT &&
         calEvent.getEventId() != CalEvent.LIST_EVENTS )
      clientPanel.resetCreateEventPanel();
  }

  public static void main(String argv[]) throws InterruptedException
  {
    /* create a new calendar client object for the user interface */
    CalendarClient client = null;
    try
    {
      client = new CalendarClient( argv[0], argv[1] );
    }
    catch(IOException ex)
    {
        System.out.println("CalendarClient: main -> " + ex);
    }
    /* start up the gui and action listener here */
    try
    {
      UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
      MetalLookAndFeel.setCurrentTheme( new DefaultMetalTheme() );
    }
    catch (Exception ex)
    {
      System.out.println("CalendarClient: main -> " + ex);
    }
    /* create add the main client panel to the client user interface */
    ClientPanel clientPanel = new ClientPanel();
    Dimension d = new Dimension( 700, 1000 );
    clientPanel.setSize( d );
    clientPanel.addCalEventListener( client );
    client.setClientPanel( clientPanel );

    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
    frame.setSize( d );
    frame.setContentPane( clientPanel );
    frame.setResizable( false );
    frame.setTitle( "Calendar Client" );
    frame.pack();
    frame.setVisible( true );

    while ( true )
    {
      boolean found = false;

      ArrayList newUsers = client.list();
      ArrayList oldUsers = clientPanel.getUsers();
      Iterator newIt = newUsers.iterator();
      Iterator oldIt = oldUsers.iterator();
      while ( newIt.hasNext() )
      {
        User n = (User)newIt.next();
        while ( oldIt.hasNext() )
        {
          User u = (User)oldIt.next();
          if ( n.getId() == u.getId() )
          {
            found = true;
            break;
          }
        }
        if ( !found ) break;
      }
      if ( !found ) clientPanel.setUsers( newUsers );
      Thread.sleep( 1000 );
    }
  }
}
